package gu.sql2java.geometry;

import java.nio.ByteBuffer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Set;

import com.google.common.base.Strings;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBConstants;
import com.vividsolutions.jts.io.WKTReader;
import static gu.sql2java.Sql2javaSupport.getBytesInBuffer;

/**
 * 存储格式WKB,WKT与Geometry对象之间的互相转换实现基类,
 * 对于不同的数据库，应该继承此类根据数据库存储WKB方式的不同提供合适的实现<br>
 * 子类必须实现{@link #toWKB(Object)},{@link #fromWKB(byte[])},{@link #getGeomtyColumnTypes()}方法,
 * 否则{@link #toWKB(Object)},{@link #fromWKB(byte[])}总是返回{@code null},
 * {@link #getGeomtyColumnTypes()}方法总是返回空
 * @author guyadong
 * @since 3.18.0
 */
public class GeometryDataCodec implements WKBConstants{

    GeometryDataCodec() {
    }
    /**
     * 将{@link Geometry}类型转为适合数据库存储的二进制格式
     * @param <T>
     * @param input
     */
    public <T extends Geometry>byte[] toWKB(T input) {
        return null;
    }
    /**
     * 将WKB格式的二进制数据解析为{@link Geometry}对象
     * @param binary
     * @throws ParseException
     */
    public Geometry fromWKB(byte[] binary) throws ParseException {
        return null;
    }
    public final <T extends Geometry>T fromWKB(byte[] binary, Class<T> targetType) throws ParseException {
        return targetType.cast(fromWKB(binary));
    }
    public final Geometry fromWKB(Object input) throws ParseException {
        if(input instanceof Geometry) {
            return (Geometry)input;
        }
        if(input instanceof byte[]) {
            return fromWKB((byte[])input);
        }else if(input instanceof ByteBuffer) {
            return fromWKB(getBytesInBuffer((ByteBuffer)input));
        }
        return null;
    }
    public final <T extends Geometry>T fromWKB(Object input, Class<T> targetType) throws ParseException {
        return targetType.cast(fromWKB(input));
    }
    public final Geometry fromWKT(String input) throws ParseException  {
        if(Strings.isNullOrEmpty(input)) {
            return null;  
        }
        WKTReader reader = new WKTReader();
        return reader.read(input);
    }
    public final <T extends Geometry> T fromWKT(String input, Class<T> targetType) throws ParseException  {
        return targetType.cast(fromWKT(input));
    }
    public final <T extends Geometry> T fromWKTUnchecked(String input, Class<T> targetType) {
    	try {
			return fromWKT(input,targetType);
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}
    }
    public final <T extends Geometry> String toWKT(T input){
    	return null == input ? null : input.toText();
    }
    public final String toWKT(Object input) throws ParseException{
    	if(null == input) {
    		return null;  
    	}
    	if(input instanceof Geometry) {
    		return ((Geometry)input).toText();
    	}else if(input instanceof String){
    		return (String)input;
    	}
    	return fromWKB(input).toText();
    }
    public final byte[] toWKB(Object value) {
        if(value instanceof Geometry) {
            return toWKB((Geometry)value);
        }else if(value instanceof String) {
            try {
                return toWKB(fromWKT((String)value));
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }else if(null != value) {
            throw new IllegalArgumentException("UNSUPPORT TYPE " + value.getClass().getName());
        }
        return null;
    }
    /**
     * 返回数据库支持的所有空间(几何)数据字段类型,默认返回空集合
     */
    public Set<String> getGeomtyColumnTypes() {
        return Collections.emptySet();
    }
    public final boolean isGeometryDataType(String typeName) throws SQLException {
        return Strings.isNullOrEmpty(typeName) ? false : getGeomtyColumnTypes().contains(typeName.toUpperCase());
    }
    public final boolean isGeometryDataType(ResultSet rs,int columnIndex) throws SQLException {
        String typeName = rs.getMetaData().getColumnTypeName(columnIndex);
        return isGeometryDataType(typeName);
    }
    /**
     * 读取数据记录指定字段的值转为空间数据对象,子类需要重写此方法
     * @param rs
     * @param columnIndex
     * @throws SQLException
     */
    public Object readGeometryData(ResultSet rs, int columnIndex) throws SQLException {
        return null;
    }
    /**
     * 默认实例
     */
    public static final GeometryDataCodec DEFAULT_INSTANCE = new GeometryDataCodec();
}
